﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using ConfigCreate.Model;
using HandyControl.Properties.Langs;
using HandyControl.Tools;
using HandyControl.Tools.Extension;
using static System.Environment;
using Binding = System.Windows.Data.Binding;
using ComboBox = System.Windows.Controls.ComboBox;
using Brush = System.Windows.Media.Brush;
using System.Dynamic;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.TrayNotify;
using HorizontalAlignment = System.Windows.HorizontalAlignment;

namespace ConfigCreate.Controls
{
    /// <summary>
    /// CreateControlPanel.xaml 的交互逻辑
    /// </summary>
    public partial class CreateControlBasePanel : System.Windows.Controls.UserControl, INotifyPropertyChanged
    {
        /// <summary>
        /// 属性变动
        /// </summary>
        public event PropertyChangedEventHandler? PropertyChanged;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="propertyName"></param>
        public void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public CreateControlBasePanel()
        {
            InitializeComponent();
            
            tv.AddHandler(TreeViewItem.MouseLeftButtonUpEvent, new RoutedEventHandler(TvItem_MouseLeftButtonUp));
        }

        #region 字段

        /// <summary>
        /// 内容变化通知
        /// </summary>
        public Action<CreateControlBasePanel, object>? Notification;

        /// <summary>
        /// 绑定状态下绑定树列表设置函数
        /// </summary>
        public Func<object, GroupHeader, List<BindingData>>? BindingDataFunc;

        #endregion

        #region 属性
        /// <summary>
        /// 标题宽度
        /// </summary>
        public static DependencyProperty TitleWidthProperty = DependencyProperty.Register("TitleWidth", typeof(double), typeof(CreateControlBasePanel), new FrameworkPropertyMetadata(120d));
        /// <summary>
        /// 标题宽度
        /// </summary>
        public double TitleWidth
        {
            get { return (double)GetValue(TitleWidthProperty); }
            set { SetValue(TitleWidthProperty, value); }
        }

        /// <summary>
        /// 标题
        /// </summary>
        public static DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(CreateControlBasePanel), new FrameworkPropertyMetadata("标题"));
        /// <summary>
        /// 标题
        /// </summary>
        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); UpdateToolTip(); }
        }

        /// <summary>
        /// 当前是否为绑定模式
        /// </summary>
        public static DependencyProperty IsFormBindingProperty = DependencyProperty.Register("IsFormBinding", typeof(bool), typeof(CreateControlBasePanel), new FrameworkPropertyMetadata(false));
        /// <summary>
        /// 当前是否为绑定模式
        /// </summary>
        public bool IsFormBinding
        {
            get { return (bool)GetValue(IsFormBindingProperty); }
            set { SetValue(IsFormBindingProperty, value); }
        }

        /// <summary>
        /// 是否为必填项
        /// </summary>
        public static DependencyProperty NecessaryProperty = DependencyProperty.Register("Necessary", typeof(bool), typeof(CreateControlBasePanel), new FrameworkPropertyMetadata(false));
        /// <summary>
        /// 是否为必填项
        /// </summary>
        public bool Necessary
        {
            get { return (bool)GetValue(NecessaryProperty); }
            set { SetValue(NecessaryProperty, value); }
        }

        /// <summary>
        /// 是否显示切换绑定-自定值按钮
        /// </summary>
        public static DependencyProperty ShowSwitchButProperty = DependencyProperty.Register("ShowSwitchBut", typeof(bool), typeof(CreateControlBasePanel), new FrameworkPropertyMetadata(false));
        /// <summary>
        /// 是否显示切换绑定-自定值按钮
        /// </summary>
        public bool ShowSwitchBut
        {
            get { return (bool)GetValue(ShowSwitchButProperty); }
            set { SetValue(ShowSwitchButProperty, value); }
        }

        /// <summary>
        /// 绑定模式下，绑定路径
        /// </summary>
        public static DependencyProperty BindingRoutePathProperty = DependencyProperty.Register("BindingRoutePath", typeof(string), typeof(CreateControlBasePanel), new FrameworkPropertyMetadata(string.Empty));
        /// <summary>
        /// 绑定模式下，绑定路径
        /// </summary>
        public string BindingRoutePath
        {
            get { return (string)GetValue(BindingRoutePathProperty); }
            set { SetValue(BindingRoutePathProperty, value); }
        }

        /// <summary>
        /// 纯面板模式，控件内只显示字控件，没有标题等其它
        /// </summary>
        public static DependencyProperty IsPanelModeProperty = DependencyProperty.Register("IsPanelMode", typeof(bool), typeof(CreateControlBasePanel), new FrameworkPropertyMetadata(false));
        /// <summary>
        /// 纯面板模式，控件内只显示字控件，没有标题等其它
        /// </summary>
        public bool IsPanelMode
        {
            get { return (bool)GetValue(IsPanelModeProperty); }
            set { SetValue(IsPanelModeProperty, value); }
        }

        /// <summary>
        /// 备注
        /// </summary>
        public static DependencyProperty RemarkProperty = DependencyProperty.Register("Remark", typeof(string), typeof(CreateControlBasePanel), new FrameworkPropertyMetadata(string.Empty));
        /// <summary>
        /// Remark内容
        /// </summary>
        public string Remark
        {
            get { return (string)GetValue(RemarkProperty); }
            set { SetValue(RemarkProperty, value); UpdateToolTip(); }
        }

        /// <summary>
        /// 绑定的属性名路径
        /// </summary>
        public string PropertyPath { get; internal set; } = string.Empty;

        /// <summary>
        /// 绑定对象
        /// </summary>
        public object? BindingObject { get; private set; }

        /// <summary>
        /// 当前的控件类型
        /// </summary>
        public ControlType ControlType { get; private set; }

        /// <summary>
        /// 分类和分组
        /// </summary>
        public GroupHeader GroupHeader { get; set; }

        /// <summary>
        /// 在保存配置时是否把该值写到保存配置中
        /// </summary>
        public bool SaveConfig { get; set; } = true;

        private string? edit_notification_definition = null;
        /// <summary>
        /// 数据值修改通过定义名称
        /// </summary>
        public string? EditNotificationDefinition
        {
            get { return edit_notification_definition; }
            set { edit_notification_definition = value; }
        }

        /// <summary>
        /// 控件内容变化通知外部
        /// </summary>
        public static DependencyProperty IsContentChangedNotificationProperty = DependencyProperty.Register("IsContentChangedNotification", typeof(bool), typeof(CreateControlBasePanel), new FrameworkPropertyMetadata(false));
        /// <summary>
        /// 控件内容变化通知外部
        /// </summary>
        public bool IsContentChangedNotification
        {
            get { return (bool)(GetValue(IsContentChangedNotificationProperty)); }
            set { SetValue(IsContentChangedNotificationProperty, value); }
        }

        /// <summary>
        /// 控件内容变化通知外部
        /// </summary>
        public static DependencyProperty ContentEnableProperty = DependencyProperty.Register("ContentEnable", typeof(bool), typeof(CreateControlBasePanel), new FrameworkPropertyMetadata(true));
        /// <summary>
        /// 控件内容变化通知外部
        /// </summary>
        public bool ContentEnable
        {
            get { return (bool)(GetValue(ContentEnableProperty)); }
            set { SetValue(ContentEnableProperty, value); }
        }


        /// <summary>
        /// 控件当前值
        /// </summary>
        public object? PanelValue
        {
            get
            {
                if (BindingObject == null)
                    return null;
                var pi = BindingObject.GetType().GetProperty(PropertyPath);
                if (pi == null)
                    return null;
                return pi.GetValue(BindingObject);
            }
        }

        #endregion

        private object main_content = new object();
        /// <summary>
        /// 主控件(如果为自定义控件，此值为模型)
        /// </summary>
        public object MainContent { get { return main_content; } set { main_content = value; OnPropertyChanged(); } }

        private List<BindingData> data_list = new List<BindingData>() { new BindingData() { NodeName = "无", RoutePath = "" } };
        /// <summary>
        /// 绑定列表树
        /// </summary>
        public List<BindingData> DataList
        {
            get { return data_list; }
            set
            {
                data_list = value;
                if (data_list == null || data_list.Count == 0)
                {
                    if (data_list == null)
                        data_list = new List<BindingData>();
                    data_list.Add(new BindingData() { NodeName = "无", RoutePath = "" });
                }
                else if (data_list.Count > 0 || string.IsNullOrEmpty(data_list[0].RoutePath))
                {
                    data_list.Insert(0, new BindingData() { NodeName = "无", RoutePath = "" });
                }
                OnPropertyChanged();
            }
        }

        /// <summary>
        /// 权限列表
        /// </summary>
        internal ConcurrentDictionary<RolePermissionType, HashSet<string>> RolePermission { get; } = new ConcurrentDictionary<RolePermissionType, HashSet<string>>();

        internal bool CheckRolePermission(RolePermissionType type, string? role_name)
        {
            if (string.IsNullOrEmpty(role_name))
                return true;
            if (!RolePermission.TryGetValue(type, out var tl))
                return true;
            return tl.Contains(role_name);
        }

        private void ContentChangedNotification(object? value)
        {
            if (value != null)
            {
                if (IsContentChangedNotification && Notification != null)
                    Notification(this, value);
            }
        }

        private void BtuBinding_Click(object sender, RoutedEventArgs e)
        {
            if (BindingDataFunc != null)
                DataList = BindingDataFunc(BindingObject, GroupHeader);
            MyPopup.HorizontalOffset = BtuBinding.ActualWidth - MyPopup.Width + 10;
            MyPopup.IsOpen = true;
        }

        private void TvItem_MouseLeftButtonUp(object sender, RoutedEventArgs e)
        {
            if (e.OriginalSource != null && e.OriginalSource is TextBlock tb)
            {
                if (tb.Tag != null && tb.Tag is BindingData item && item.ChildNodes == null)
                {
                    BindingRoutePath = item.RoutePath;
                    MyPopup.IsOpen = false;
                }
            }
        }

        private void BPopup_Opened(object sender, EventArgs e)
        {
            Dispatcher.BeginInvoke(() =>
            {
                TreeViewItem? item = GetItem();
                if (item != null)
                {
                    behavior.SelectedItem = item;
                    item.Focus();
                }
            }, System.Windows.Threading.DispatcherPriority.Background);
        }

        private TreeViewItem? GetItem()
        {
            List<BindingData> tl = new List<BindingData>();
            BindingData? data = GetItem(data_list, tl);
            if (data != null)
                return GetItem(tv.ItemContainerGenerator, tl);
            return null;
        }

        private TreeViewItem? GetItem(ItemContainerGenerator container, List<BindingData> tl)
        {
            ItemContainerGenerator con = container;
            TreeViewItem? item = null;
            bool return_null = false;
            foreach (BindingData data in tl)
            {
                Dispatcher.BeginInvoke(() =>
                {
                    var depObj = con.ContainerFromItem(data);
                    if (depObj == null)
                    {
                        return_null = true;
                        return;
                    }
                    item = (TreeViewItem)depObj;
                    item.IsExpanded = true;
                    con = item.ItemContainerGenerator;

                }, System.Windows.Threading.DispatcherPriority.Background).Wait();

                if (return_null)
                    return null;
            }
            return item;
        }

        private BindingData? GetItem(List<BindingData>? list, List<BindingData> roure_list)
        {
            BindingData? data = list?.FirstOrDefault(it => it.RoutePath == BindingRoutePath && it.ChildNodes == null);
            if (data != null)
            {
                roure_list.Add(data);
                return data;
            }
            var tl = list.Where(it => it.ChildNodes != null);
            foreach(var it in tl)
            {
                roure_list.Add(it);
                data = GetItem(it.ChildNodes, roure_list);
                if (data != null)
                    return data;
                roure_list.Remove(it);
            }
            return null;
        }

        private void UpdateToolTip()
        {
            if (!string.IsNullOrEmpty(Title))
            {
                Grid grid = (Grid)this.Resources["PanelToolTip"];
                TextBlock tb = (TextBlock)grid.Children[0];
                tb.Text = Title;
                tb = (TextBlock)grid.Children[1];
                tb.Text = Remark;
                if (TitleWidth == 0)
                    content.ToolTip = grid;
                else
                    g_title.ToolTip = grid;
            }
        }

        #region 添加控件

        private MyPropertyInfo CheckPars(object binding_object, string property_path)
        {
            if (string.IsNullOrEmpty(property_path))
                throw new ArgumentNullException(nameof(property_path));
            if (binding_object == null)
                throw new ConfigException("绑定对象不能为空");
            if (binding_object is ExpandoObject || binding_object is IDictionary<string, object>)
            {
                IDictionary<string, object> data = (IDictionary<string, object>)binding_object;
                return new MyPropertyInfo(property_path, data);
            }
            Type type = binding_object.GetType();
            if (!type.IsClass)
                throw new ConfigException("绑定对象必须为类对象");
            if (type.Equals(typeof(string)))
                throw new ConfigException("绑定对象不能为字符串");
            var pi = type.GetProperty(property_path);
            if (pi == null)
                throw new ConfigException($"绑定对象中不存在【{property_path}】属性");
            if (!pi.CanRead)
                throw new ConfigException($"【{property_path}】属性不可读无法生成控件");
            ContentEnable = pi.CanWrite;
            return new MyPropertyInfo(pi);
        }

        /// <summary>
        /// 添加文本框
        /// </summary>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="placeholder">提示占位符</param>
        /// <param name="text_lenght">文本框中字符长度</param>
        /// <param name="show_clear_button">是否显示清空按钮</param>
        /// <param name="is_readonly">是否为只读</param>
        /// <param name="is_multiline">是否多行文本</param>
        /// <param name="height">文本高度</param>
        public void AddTextBox(object binding_object, string property_path, string? placeholder = null, int? text_lenght = null, bool show_clear_button = true, bool is_readonly = false, bool is_multiline = false, double height = 60d)
        {
            var pi = CheckPars(binding_object, property_path);
            CheckBindingType(binding_object, property_path, "文本框控件只支持字符串类型进行绑定", typeof(string));
            HandyControl.Controls.TextBox textBox = new HandyControl.Controls.TextBox();
            Binding myBinding = new Binding(property_path);
            myBinding.Source = binding_object;
            if (!ContentEnable || !pi.CanWrite)
                myBinding.Mode = BindingMode.OneWay;
            textBox.SetBinding(HandyControl.Controls.TextBox.TextProperty, myBinding);
            //
            textBox.TextChanged += (sender, e) => ContentChangedNotification(textBox.Text);
            //
            if (text_lenght.HasValue)
                textBox.MaxLength = text_lenght.Value;
            if (show_clear_button)
                HandyControl.Controls.InfoElement.SetShowClearButton(textBox, true);
            //textBox.Style = this.Resources["TextBoxExtend"] as Style;
            if (!string.IsNullOrEmpty(placeholder))
                HandyControl.Controls.InfoElement.SetPlaceholder(textBox, placeholder);
            textBox.IsReadOnly = is_readonly;
            if (is_multiline)
            {
                textBox.VerticalContentAlignment = VerticalAlignment.Top;
                textBox.TextWrapping = TextWrapping.Wrap;
                textBox.AcceptsReturn = true;
                textBox.Height = height;
                textBox.VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
            }
            MainContent = textBox;
            PropertyPath = property_path;
            BindingObject = binding_object;
            ControlType = ControlType.TextBox;
        }

        /// <summary>
        /// 添加数字框
        /// </summary>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="placeholder">提示占位符</param>
        /// <param name="decimal_places">小数点多少位</param>
        /// <param name="increment">获取或设置单击向上或向下按钮时，数字显示框（也称作 up-down 控件）递增或递减的值</param>
        /// <param name="maximum">获取或设置最大允许值</param>
        /// <param name="minimum">获取或设置最小允许值</param>
        /// <param name="value_format">数字显示格式</param>
        /// <param name="show_clear_button">是否显示清除按钮</param>
        /// <param name="is_readonly">是否为只读</param>
        /// <param name="rule">数据验证</param>
        public void AddNumeric(object binding_object, string property_path, string? placeholder = null, int? decimal_places = null, double? increment = null, double? maximum = null, double? minimum = null, string? value_format = null, bool show_clear_button = false, bool is_readonly = false, ValidationRule? rule = null)
        {
            var pi = CheckPars(binding_object, property_path);
            CheckBindingType(binding_object, property_path, "数字框控件只支持数字类型进行绑定", typeof(int), typeof(int?), typeof(short), typeof(short?), typeof(long), typeof(long?), typeof(float), typeof(float?), typeof(double), typeof(double?), typeof(decimal), typeof(decimal?), typeof(uint), typeof(uint?), typeof(ushort), typeof(ushort?), typeof(ulong), typeof(ulong?));
            if (binding_object is ExpandoObject && !typeof(double).Equals(pi.PropertyType)) //当绑定对象为ExpandoObject时，数字框只支持double类型，这时给它转换一下
            {
                IDictionary<string, object> tdata = (IDictionary<string, object>)binding_object;
                tdata[property_path] = Convert.ChangeType(tdata[property_path], typeof(double));
            }
            HandyControl.Controls.NumericUpDown numeric = new HandyControl.Controls.NumericUpDown();
            numeric.Style = this.Resources["NumericUpDownPlus"] as Style;
            Binding binding = new Binding(property_path);
            binding.Source = binding_object;
            if (!ContentEnable || !pi.CanWrite)
                binding.Mode = BindingMode.OneWay;
            numeric.SetBinding(HandyControl.Controls.NumericUpDown.ValueProperty, binding);
            //
            numeric.ValueChanged += (sender, e) => ContentChangedNotification(numeric.Value);
            //
            if (!string.IsNullOrEmpty(placeholder))
                HandyControl.Controls.InfoElement.SetPlaceholder(numeric, placeholder);
            if (decimal_places.HasValue && decimal_places.Value > 0)
                numeric.DecimalPlaces = decimal_places.Value;
            if (increment.HasValue)
                numeric.Increment = increment.Value;
            if (maximum.HasValue)
                numeric.Maximum = maximum.Value;
            if (minimum.HasValue)
                numeric.Minimum = minimum.Value;
            if (string.IsNullOrEmpty(value_format))
                numeric.ValueFormat = value_format;
            if (show_clear_button)
                HandyControl.Controls.InfoElement.SetShowClearButton(numeric, true);
            numeric.IsReadOnly = is_readonly;
            if (rule != null)
                binding.ValidationRules.Add(rule);

            MainContent = numeric;
            PropertyPath = property_path;
            BindingObject = binding_object;
            ControlType = ControlType.NumericUpDown;
        }

        /// <summary>
        /// 添加切换按钮
        /// </summary>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="checked_txt">选择时显示的文本</param>
        /// <param name="unchecked_txt">未选择时显示的文本</param>
        public void AddToggleButton(object binding_object, string property_path, string? checked_txt = null, string? unchecked_txt = null)
        {
            var pi = CheckPars(binding_object, property_path);
            CheckBindingType(binding_object, property_path, "切换按钮控件只支持【bool】或【bool?】类型进行绑定", typeof(bool), typeof(bool?));
            ToggleButton tbtn = new ToggleButton();
            Binding binding = new Binding(property_path);
            binding.Source = binding_object;
            if (!ContentEnable || !pi.CanWrite)
                binding.Mode = BindingMode.OneWay;
            tbtn.SetBinding(ToggleButton.IsCheckedProperty, binding);
            tbtn.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
            //
            tbtn.Checked += (sender, e) => ContentChangedNotification(true);
            tbtn.Unchecked += (sender, e) => ContentChangedNotification(false);
            //
            if (!string.IsNullOrEmpty(checked_txt) && !string.IsNullOrEmpty(unchecked_txt))
            {
                tbtn.BorderThickness = new Thickness(0);
                tbtn.Style = this.Resources["ToggleButtonFlip"] as Style;
                Border border_check = (Border)this.Resources["BorderUnChecked"];
                ((TextBlock)border_check.Child).Text = unchecked_txt;
                tbtn.Content = border_check;
                Border border_uncheck = (Border)this.Resources["BorderChecked"];
                ((TextBlock)border_uncheck.Child).Text = checked_txt;
                HandyControl.Controls.StatusSwitchElement.SetCheckedElement(tbtn, border_uncheck);
            }
            else
            {
                tbtn.Style = this.Resources["ToggleButtonSwitch"] as Style;
            }

            MainContent = tbtn;
            PropertyPath = property_path;
            BindingObject = binding_object;
            ControlType = ControlType.ToggleButton;
        }

        /// <summary>
        /// 添加密码框
        /// </summary>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="placeholder">提示占位符</param>
        /// <param name="password_len">密码框字符长度</param>
        /// <param name="show_eye_button">是否显示查看密码按钮</param>
        /// <param name="show_clear_button">是否显示清空按钮</param>
        /// <param name="password_char">密码字符</param>
        public void AddPasswordBox(object binding_object, string property_path, string? placeholder = null, int? password_len = null, bool show_eye_button = true, bool show_clear_button = true, char? password_char = null)
        {
            var pi = CheckPars(binding_object, property_path);
            CheckBindingType(binding_object, property_path, "密码框控件只支持字符串类型进行绑定", typeof(string));
            HandyControl.Controls.PasswordBox pbox = new HandyControl.Controls.PasswordBox() { IsSafeEnabled = false };
            Binding binding = new Binding(property_path);
            binding.Source = binding_object;
            if (!ContentEnable || !pi.CanWrite)
                binding.Mode = BindingMode.OneWay;
            pbox.SetBinding(HandyControl.Controls.PasswordBox.UnsafePasswordProperty, binding);
            //
            pbox.VerifyFunc = (password) =>
            {
                ContentChangedNotification(password);
                return HandyControl.Data.OperationResult.Success();
            };
            //
            if (!string.IsNullOrEmpty(placeholder))
                HandyControl.Controls.InfoElement.SetPlaceholder(pbox, placeholder);
            if (password_len.HasValue)
                pbox.MaxLength = password_len.Value;
            pbox.ShowEyeButton = show_eye_button;
            pbox.ShowClearButton = show_clear_button;
            if (password_char.HasValue)
                pbox.PasswordChar = password_char.Value;

            MainContent = pbox;
            PropertyPath = property_path;
            BindingObject = binding_object;
            ControlType = ControlType.PasswordBox;
        }

        /// <summary>
        /// 添加组合框
        /// </summary>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="item_type">添加项类型</param>
        /// <param name="tl">数据源</param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ConfigException"></exception>
        public void AddComboBox(object binding_object, string property_path, Type item_type, List<ComboBoxData> tl)
        {
            var pi = CheckPars(binding_object, property_path);
            if (tl == null)
                throw new ArgumentNullException(nameof(tl));
            if (pi != null && !item_type.IsAssignableFrom(pi.PropertyType))
                throw new ConfigException($"组合框控件绑定的内容必须为指定的【{item_type.FullName}】类型");
            ComboBox cbox = new ComboBox();
            Binding binding = new Binding(property_path);
            binding.Source = binding_object;
            if (!ContentEnable || pi?.CanWrite == false)
                binding.Mode = BindingMode.OneWay;
            cbox.SetBinding(ComboBox.SelectedValueProperty, binding);
            cbox.DisplayMemberPath = "Name";
            cbox.SelectedValuePath = "Value";
            
            cbox.ItemsSource = tl;
            HandyControl.Controls.DropDownElement.SetConsistentWidth(cbox, false);
            //
            cbox.SelectionChanged += (sender, e) => ContentChangedNotification(tl[cbox.SelectedIndex]);
            //

            MainContent = cbox;
            PropertyPath = property_path;
            BindingObject = binding_object;
            ControlType = ControlType.ComboBox;
        }

        /// <summary>
        /// 添加日期选择框
        /// </summary>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        public void AddDatePicker(object binding_object, string property_path)
        {
            
            var pi = CheckPars(binding_object, property_path);
            CheckBindingType(binding_object, property_path, "日期选择框控件只支持日期类型类型进行绑定", typeof(DateTime), typeof(DateTime?), typeof(DateTimeOffset), typeof(DateTimeOffset?));

            DatePicker date = new DatePicker();
            Binding binding = new Binding(property_path);
            binding.Source = binding_object;
            if (!ContentEnable || !pi.CanWrite)
                binding.Mode = BindingMode.OneWay;
            date.SetBinding(DatePicker.SelectedDateProperty, binding);
            //
            if (date.SelectedDate != null)
                date.SelectedDateChanged += (sender, e) => ContentChangedNotification(date.SelectedDate.Value);
            //
            MainContent = date;
            PropertyPath = property_path;
            BindingObject = binding_object;
            ControlType = ControlType.DatePicker;
        }

        /// <summary>
        /// 添加时间选择框
        /// </summary>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="date_timeformat">日期显示格式</param>
        public void AddDateTimePicker(object binding_object, string property_path, string? date_timeformat = null)
        {
            var pi = CheckPars(binding_object, property_path);
            CheckBindingType(binding_object, property_path, "日期时间选择控件只支持日期类型进行绑定", typeof(DateTime), typeof(DateTime?), typeof(DateTimeOffset), typeof(DateTimeOffset?));

            HandyControl.Controls.DateTimePicker dateTimePicker = new HandyControl.Controls.DateTimePicker();
            Binding binding = new Binding(property_path);
            binding.Source = binding_object;
            binding.Source = binding_object;
            if (!ContentEnable || !pi.CanWrite)
                binding.Mode = BindingMode.OneWay;
            dateTimePicker.SetBinding(HandyControl.Controls.DateTimePicker.SelectedDateTimeProperty, binding);

            //
            if (dateTimePicker.SelectedDateTime != null)
                dateTimePicker.SelectedDateTimeChanged += (sender, e) => ContentChangedNotification(dateTimePicker.SelectedDateTime.Value);
            //
            if (!string.IsNullOrEmpty(date_timeformat))
                dateTimePicker.DateTimeFormat = date_timeformat;

            MainContent = dateTimePicker;
            PropertyPath = property_path;
            BindingObject = binding_object;
            ControlType = ControlType.DateTimePicker;
        }

        /// <summary>
        /// 添加多选组合框
        /// </summary>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="item_type">子项类型</param>
        /// <param name="tl">数据源</param>
        /// <param name="show_select_all_button">是否显示选择所有框</param>
        /// <param name="show_clear_button">是否显示清空按钮</param>
        /// <exception cref="ConfigException"></exception>
        public void AddMultipleComboBox(object binding_object, string property_path, Type item_type, List<ComboBoxData> tl, bool show_select_all_button = true, bool show_clear_button = true)
        {
            var pi = CheckPars(binding_object, property_path);
            if (pi.PropertyType?.IsGenericType == false || pi.PropertyType?.Name != "List`1" || !item_type.IsAssignableFrom(pi.PropertyType.GenericTypeArguments[0]))
                throw new ConfigException($"多选组合框控件只支持列表类型进行绑定或并不是指定【{item_type.FullName}】类型");

            HandyControl.Controls.CheckComboBox ccbox = new HandyControl.Controls.CheckComboBox();
            ccbox.SelectionMode = System.Windows.Controls.SelectionMode.Multiple;
            ccbox.DisplayMemberPath = "Name";
            ccbox.SelectedValuePath = "Value";
            ccbox.ItemsSource = tl;
            IList? slist = (IList?)pi?.GetValue(binding_object);
            if (slist == null)
            {
                slist = (IList?)Activator.CreateInstance(pi?.PropertyType ?? typeof(IList));
                pi?.SetValue(binding_object, slist);
            }
            if (slist != null)
            {
                foreach (var data in slist)
                {
                    ComboBoxData? cdata = tl?.FirstOrDefault(it => it.Value.Equals(data));
                    if (cdata != null)
                        ccbox.SelectedItems.Add(cdata); ;
                }

                ccbox.SelectionChanged += (s, e) =>
                {
                    foreach (ComboBoxData d in e.AddedItems)
                        slist.Add(d.Value);
                    foreach (ComboBoxData d in e.RemovedItems)
                        slist.Remove(d.Value);
                    //
                    ContentChangedNotification(ccbox.SelectedItems);
                };
            }

            ccbox.Style = this.Resources["CheckComboBoxPlus"] as Style;
            ccbox.ShowSelectAllButton = show_clear_button;
            ccbox.ShowClearButton = show_clear_button;

            MainContent = ccbox;
            PropertyPath = property_path;
            BindingObject = binding_object;
            ControlType = ControlType.CheckComboBox;
        }

        /// <summary>
        /// 添加IP地址框
        /// </summary>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="is_readonly">是否只读模式</param>
        public void AddIpAddressTextBox(object binding_object, string property_path, bool is_readonly = false)
        {
            var pi = CheckPars(binding_object, property_path);
            CheckBindingType(binding_object, property_path, "IP地址框控件只支持字符串类型进行绑定", typeof(string));
            CreateControlIpAddressTextBox ip = new CreateControlIpAddressTextBox();
            ip.IsReadOnly = is_readonly;
            ip.Style = this.Resources["IpAddressTextBox"] as Style;
            ip.OnApplyTemplate();
            Binding binding = new Binding(property_path);
            binding.Source = binding_object;
            if (!ContentEnable || !pi.CanWrite)
                binding.Mode = BindingMode.OneWay;
            else
                binding.Mode = BindingMode.TwoWay;
            ip.SetBinding(CreateControlIpAddressTextBox.TextProperty, binding);
            //
            ip.IpAddressChanged += (sender, v) => ContentChangedNotification(ip.Text);
            //
            MainContent = ip;
            PropertyPath = property_path;
            BindingObject = binding_object;
            ControlType = ControlType.IpAddressTextBox;
        }

        /// <summary>
        /// 添加打开文件
        /// </summary>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="dialog_title">打开对话框标题</param>
        /// <param name="dialog_filter">打开对话框过滤项</param>
        /// <param name="is_multiselect">是否为多选</param>
        /// <param name="is_readonly">其中文本框是否只读</param>
        public void AddOpenFile(object binding_object, string property_path, string? dialog_title = null, string? dialog_filter = null, bool is_multiselect = false, bool is_readonly = false)
        {
            var pi = CheckPars(binding_object, property_path);
            if (!is_multiselect)
                CheckBindingType(binding_object, property_path, "单选文件选择框控件只支持字符串类型进行绑定", typeof(string));
            else
                CheckBindingType(binding_object, property_path, "多选文件选择框控件只支持字符串列表类型进行绑定", typeof(List<string>));

            CreateControlOpenFile openFile = new CreateControlOpenFile();
            if (!string.IsNullOrEmpty(dialog_title))
                openFile.DialogTitle = dialog_title ?? string.Empty;
            if (!string.IsNullOrEmpty(dialog_filter))
                openFile.DialogFilter = dialog_filter ?? string.Empty;
            openFile.IsReadOnly = is_readonly;
            openFile.Multiselect = is_multiselect;

            if (!is_multiselect)
            {
                Binding binding = new Binding(property_path);
                binding.Source = binding_object;
                if (!ContentEnable || !pi.CanWrite)
                    binding.Mode = BindingMode.OneWay;
                else
                    binding.Mode = BindingMode.TwoWay;
                openFile.SetBinding(CreateControlOpenFile.SelectedFileNameProperty, binding);
            }
            else
            {
                List<string>? list = pi.GetValue(binding_object) as List<string>;
                if (list == null)
                    list = new List<string>();
                if (list.Count > 0)
                {
                    openFile.ShowBox = true;
                    foreach (var file in list)
                        openFile.DataList.Add(file);
                    openFile.SelectedFileName = list[list.Count - 1];
                }
            }

            openFile.SelectedFileChanged += (sender, file_list) =>
            {
                if (!is_multiselect)
                {
                    //pi.SetValue(binding_object, openFile.SelectedFileName);
                    //
                    ContentChangedNotification(openFile.SelectedFileName);
                }
                else
                {
                    List<string>? tl = pi.GetValue(binding_object) as List<string>;
                    if (tl == null)
                    {
                        tl = new List<string>();
                        pi.SetValue(binding_object, tl);
                    }
                    tl.Clear();
                    tl.AddRange(file_list);
                    //
                    ContentChangedNotification(file_list.ToArray());
                }
            };


            MainContent = openFile;
            PropertyPath = property_path;
            BindingObject = binding_object;
            ControlType = ControlType.OpenFile;
        }

        /// <summary>
        /// 添加保存对话框
        /// </summary>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="dialog_title">对话框标题</param>
        /// <param name="dialog_filter">对话框过滤项</param>
        /// <param name="is_readonly">其中文本框是否只读</param>
        /// <param name="default_ext">默认扩展名</param>
        /// <param name="initial_directory">初始化目录</param>
        public void AddSaveFile(object binding_object, string property_path, string? dialog_title = null, string? dialog_filter = null, bool is_readonly = false, string? default_ext = null, string? initial_directory = null)
        {
            var pi = CheckPars(binding_object, property_path);
            CheckBindingType(binding_object, property_path, "文件保存框控件只支持字符串类型进行绑定", typeof(string));

            CreateControlSaveFile saveFile = new CreateControlSaveFile();
            if(!string.IsNullOrEmpty(dialog_title))
                saveFile.DialogTitle = dialog_title ?? string.Empty;
            if (!string.IsNullOrEmpty(dialog_filter))
                saveFile.DialogFilter = dialog_filter ?? string.Empty;
            if (!string.IsNullOrEmpty(default_ext))
                saveFile.DefaultExt = default_ext ?? string.Empty;
            if (!string.IsNullOrEmpty(initial_directory))
                saveFile.InitialDirectory = initial_directory ?? string.Empty;
            saveFile.IsReadOnly = is_readonly;

            Binding binding = new Binding(property_path);
            binding.Source = binding_object;
            if (!ContentEnable || !pi.CanWrite)
                binding.Mode = BindingMode.OneWay;
            else
                binding.Mode = BindingMode.TwoWay;
            saveFile.SetBinding(CreateControlSaveFile.SelectedFileNameProperty, binding);

            //
            saveFile.SelectedFileChanged += (sender, v) => ContentChangedNotification(saveFile.SelectedFileName);
            //
            MainContent = saveFile;
            PropertyPath = property_path;
            BindingObject = binding_object;
            ControlType = ControlType.SaveFile;
        }

        /// <summary>
        /// 添加目录选择框
        /// </summary>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="dialog_description">对话框标题</param>
        /// <param name="show_new_folder_button">是否显示新建文件夹按钮</param>
        /// <param name="root_folder">对话框根目录</param>
        /// <param name="is_readonly">其中文本框是否只读</param>
        public void AddOpenFolder(object binding_object, string property_path, string? dialog_description = null, bool show_new_folder_button = true, SpecialFolder root_folder = SpecialFolder.Desktop, bool is_readonly = false)
        {
            var pi = CheckPars(binding_object, property_path);
            CheckBindingType(binding_object, property_path, "目录选择框控件只支持字符串类型进行绑定", typeof(string));

            CreateControlOpenFolder openFolder = new CreateControlOpenFolder();
            if (!string.IsNullOrEmpty(dialog_description))
                openFolder.Description = dialog_description ?? string.Empty;
            openFolder.ShowNewFolderButton = show_new_folder_button;
            openFolder.RootFolder = root_folder;
            openFolder.IsReadOnly = is_readonly;

            Binding binding = new Binding(property_path);
            binding.Source = binding_object;
            if (!ContentEnable || !pi.CanWrite)
                binding.Mode = BindingMode.OneWay;
            else
                binding.Mode = BindingMode.TwoWay;
            openFolder.SetBinding(CreateControlOpenFolder.SelectedPathProperty, binding);

            //
            openFolder.SelectedPathChanged += (sender, v) => ContentChangedNotification(openFolder.SelectedPath);
            //
            MainContent = openFolder;
            PropertyPath = property_path;
            BindingObject = binding_object;
            ControlType = ControlType.OpenFolder;
        }

        /// <summary>
        /// 添加标签框
        /// </summary>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="style_key">样式Key</param>
        /// <param name="horizontal_alignment">横向靠齐方式</param>
        /// <param name="font_size">文字大小</param>
        /// <param name="foreground">前渲染</param>
        /// <param name="background">背景渲染</param>
        /// <param name="font_weight">文字样式</param>
        /// <param name="font_family">文字字体</param>
        /// <exception cref="ConfigException"></exception>
        public void AddTextBlock(object binding_object, string property_path, string? style_key = null, HorizontalAlignment horizontal_alignment = HorizontalAlignment.Left, double? font_size = null, Brush? foreground = null, Brush? background = null, string? font_weight = null, string? font_family = null)
        {
            var pi = CheckPars(binding_object, property_path);
            CheckBindingType(binding_object, property_path, "标签框控件只支持字符串类型进行绑定", typeof(string));

            TextBlock label = new TextBlock();
            if (pi != null)
                label.Text = pi.GetValue(binding_object)?.ToString();
            label.VerticalAlignment = VerticalAlignment.Stretch;
            label.HorizontalAlignment = horizontal_alignment;
            if (!string.IsNullOrEmpty(style_key))
            {
                if (!this.Resources.Contains(style_key))
                    throw new ConfigException($"资源中未发现【{style_key}】样式");
                label.Style = (Style)this.Resources[style_key];
            }
            if (background != null)
                label.Background = background;
            else
                label.Background = System.Windows.Media.Brushes.Transparent;
            if (font_size != null && font_size.Value > 0)
                label.FontSize = font_size.Value;
            if (foreground != null)
                label.Foreground = foreground;
            if (background != null)
                label.Background = background;
            if (!string.IsNullOrEmpty(font_weight))
            {
                var tpi = typeof(FontWeights).GetProperty(font_weight, BindingFlags.Static | BindingFlags.Public | BindingFlags.GetProperty);
                if (tpi == null)
                    throw new ConfigException($"FontWeights类中未找到【{font_weight}】名称的属性");
                if (tpi != null)
                    label.FontWeight = (FontWeight)tpi.GetValue(null);
            }
            if(!string.IsNullOrEmpty(font_family))
            {
                label.FontFamily = new System.Windows.Media.FontFamily(font_family);
            }

            TitleWidth = 0;
            MainContent = label;
            PropertyPath = property_path;
            BindingObject = binding_object;
            ControlType = ControlType.Label;
        }

        /// <summary>
        /// 添加自定义控件
        /// </summary>
        /// <param name="binding_object">绑定对象</param>
        /// <param name="property_path">上面对象的属性名</param>
        /// <param name="is_panel_mode">是否为纯面板模式，不显示Titile内容</param>
        public void AddCustomControl(object binding_object, string property_path, bool is_panel_mode = false)
        {
            var pi = CheckPars(binding_object, property_path);
            object? content = pi.GetValue(binding_object);
            if (content == null)
                content = pi.NewObject();
            IsPanelMode = is_panel_mode;

            MainContent = content;
            PropertyPath = property_path;
            BindingObject = binding_object;
            ControlType = ControlType.CustomControl;
        }


        #endregion

        /// <summary>
        /// 检查值是否跟当前控件的值是否相等
        /// </summary>
        /// <param name="value">待检查的值</param>
        /// <returns></returns>
        public bool CheckValue(object value)
        {
            object? obj = PanelValue;
            if (value == null || obj == null)
                return false;
            if(ControlType == ControlType.CheckComboBox || ControlType == ControlType.MultiOpenFile)
            {
                IList tl = (IList)obj;
                return tl.Contains(value);
            }
            else
            {
                return obj.Equals(value);
            } 
        }

        
        /// <summary>
        /// 必选项检查
        /// </summary>
        /// <returns></returns>
        public bool CheckNecessary()
        {
            if (Necessary)
            {
                if (IsFormBinding) //当前为绑定模式，检查绑定项就行
                {
                    return !string.IsNullOrEmpty(BindingRoutePath);
                }
                switch(ControlType)
                {
                    case ControlType.TextBox:
                        HandyControl.Controls.TextBox tbox = (HandyControl.Controls.TextBox)MainContent;
                        return !string.IsNullOrEmpty(tbox.Text);
                    case ControlType.PasswordBox:
                        HandyControl.Controls.PasswordBox pbox = (HandyControl.Controls.PasswordBox)MainContent;
                        return !string.IsNullOrEmpty(pbox.UnsafePassword);
                    case ControlType.ComboBox:
                        ComboBox cbox = (ComboBox)MainContent;
                        return cbox.SelectedIndex >= 0;
                    case ControlType.DatePicker:
                        DatePicker date = (DatePicker)MainContent;
                        return date.SelectedDate.HasValue;
                    case ControlType.DateTimePicker:
                        HandyControl.Controls.DateTimePicker time = (HandyControl.Controls.DateTimePicker)MainContent;
                        return time.SelectedDateTime.HasValue;
                    case ControlType.CheckComboBox:
                        HandyControl.Controls.CheckComboBox ccbox = (HandyControl.Controls.CheckComboBox)MainContent;
                        return ccbox.SelectedItems.Count > 0;
                    case ControlType.IpAddressTextBox:
                        CreateControlIpAddressTextBox ip = (CreateControlIpAddressTextBox)MainContent;
                        return (ip.Number1.HasValue && ip.Number2.HasValue && ip.Number3.HasValue && ip.Number4.HasValue);
                    case ControlType.OpenFile:
                        CreateControlOpenFile open = (CreateControlOpenFile)MainContent;
                        return !string.IsNullOrEmpty(open.SelectedFileName);
                    case ControlType.MultiOpenFile:
                        CreateControlOpenFile openM = (CreateControlOpenFile)MainContent;
                        return openM.DataList.Count > 0;
                    case ControlType.SaveFile:
                        CreateControlSaveFile save = (CreateControlSaveFile)MainContent;
                        return !string.IsNullOrEmpty(save.SelectedFileName);
                    case ControlType.OpenFolder:
                        CreateControlOpenFolder folder = (CreateControlOpenFolder)MainContent;
                        return !string.IsNullOrEmpty(folder.SelectedPath);
                }
            }
            return true;
        }

        private void CheckBindingType(object binding_object, string property_path, string err_msg, params Type[] types)
        {
            if (types.Length == 0)
                return;
            Type? check_type = null;
            if (binding_object is ExpandoObject || binding_object is IDictionary<string, object>)
            {
                IDictionary<string, object> data = (IDictionary<string, object>)binding_object;
                if (data.ContainsKey(property_path))
                    check_type = data[property_path]?.GetType();
            }
            else
            {
                check_type = binding_object.GetType().GetProperty(property_path)?.PropertyType;
            }
            if(check_type != null)
            {
                foreach(var type in types )
                {
                    if (type.IsAssignableFrom(check_type))
                        return;
                }
            }
            throw new ConfigException(err_msg);
        }

        /// <summary>
        /// 获取主体控件对象
        /// </summary>
        /// <returns></returns>
        public ContentControl GetMainContent()
        {
            return content;
        }

        /// <summary>
        /// 获取实际生成的控件对象
        /// </summary>
        /// <typeparam name="T">指定控件类型</typeparam>
        /// <returns></returns>
        public T? GetVisualContent<T>() where T : FrameworkElement
        {
            int count = VisualTreeHelper.GetChildrenCount(content);
            for (int i = 0; i < count; i++)
            {
                var child = VisualTreeHelper.GetChild(content, i);
                if (child is ContentPresenter presenter)
                {
                    int ccount = VisualTreeHelper.GetChildrenCount(child);
                    for (int j = 0; j < ccount; j++)
                    {
                        var innerchild = VisualTreeHelper.GetChild(child, j);
                        if (innerchild is T)
                            return (T)innerchild;
                    }
                }

            }
            return default;
        }
    }
}
